package com.author.document.view;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;

import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.text.Element;
import javax.swing.text.View;
import javax.swing.text.html.BlockView;

import com.author.surface.Surface;
import com.author.surface.TextPane;
import com.author.surface.TextPanel;

public class MultiPageSectionView extends BlockView {
	/* Count of the number of pages */
	private int				pageNumber		= 0;

	/**
	 * The width of the rectangular grey box that gives the appearance of pages,
	 * in between two pages of the section view
	 */
	public static final int	PAGE_SEPARATOR	= 5;

	/* Height of header */
	private int				headerHeight	= 0;

	/* Header */
	private JEditorPane		header			= null;

	public MultiPageSectionView(Element elem, int axis) {
		super(elem, axis);
	}

	public void layout(int width, int height) {
		super.layout(width, height);
	}

	@Override
	protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
			int[] spans) {
		super.layoutMajorAxis(targetSpan, axis, offsets, spans);
		multiPageLayout(targetSpan, axis, offsets, spans);
	}

	private void multiPageLayout(int targetSpan, int axis, int[] offsets,
			int[] spans) {
		int totalOffset = 0;
		int n = offsets.length;
		int pageNum = 0;

		/* Validate the sizes of the header and footer if they exist */
		validateHF();

		for (int i = 0; i < n; i++) {
			offsets[i] = totalOffset;
			View v = this.getView(i);

			if (v instanceof MultiPageView) {
				((MultiPageView) v).setBreakSpan(0);
				((MultiPageView) v).setAdditionalSpace(0);
			}

			if ((offsets[i] + spans[i]) > pageNum * PageConstants.PAGE_HEIGHT) {
				if (v instanceof MultiPageView) {
					MultiPageView mpv = (MultiPageView) v;

					/* Calculate the remaining span and the current offset */
					int span = (((pageNum + 1) * PageConstants.PAGE_HEIGHT) - offsets[i])
							+ pageNum
							* (PageConstants.PAGE_TOP_INSET + PageConstants.PAGE_BOTTOM_INSET);
					int offset = offsets[i]
							- (pageNum * PageConstants.PAGE_HEIGHT);

					mpv.setPageOffSet(offset);
					mpv.setBreakSpan(span);
					
					mpv.setStartPageNumber(pageNum);
					mpv.setEndPageNumber(pageNum);

					if (v instanceof MultiPageParagraphView) {
						MultiPageParagraphView mppv = (MultiPageParagraphView) v;
						mppv.layout(((MultiPageParagraphView) v).getWidth(),
								this.getHeight());
					}

					int addspace = mpv.getAdditionalSpace();
					spans[i] += addspace;

					this.setPageNumber(mpv.getEndPageNumber());
					pageNum = getPageCount();

					/*
					 * If space is added to the section view then we also need
					 * to increase the size of the canvas panel. This will make
					 * the scroll pane adjust its viewport accordingly
					 */

					if (addspace > 0) {
						Surface s = (Surface) Surface.getAppSurface();
						TextPanel textpanel = ((TextPanel) s.getTextPanel());
						JPanel canvaspanel = textpanel.getCanvasPanel();

						/*
						 * Increase height of the canvas panel to accommodate
						 * one more page
						 */
						canvaspanel.setSize(canvaspanel.getSize().width,
								canvaspanel.getSize().height
										+ PageConstants.TOTAL_PAGE_HEIGHT);
					}
				}
			}
			totalOffset = Math.min((offsets[i] + spans[i]), Integer.MAX_VALUE);
		}
	}

	public void setPageNumber(int pageNumber) {
		this.pageNumber = pageNumber;
	}

	public int getPageCount() {
		return this.pageNumber;
	}

	public float getMaximumSpan(int axis) {
		return getPreferredSpan(axis);
	}

	public float getMinimumSpan(int axis) {
		return getPreferredSpan(axis);
	}

	public float getPreferredSpan(int axis) {
		float span = 0;

		if (axis == View.X_AXIS) {
			span = PageConstants.PAGE_WIDTH;
		} else {
			int n = this.getPageCount();
			span = PageConstants.PAGE_HEIGHT
					* (n + 1)
					+ n
					* (PageConstants.PAGE_TOP_INSET + PageConstants.PAGE_BOTTOM_INSET);
		}

		return span;
	}

	@Override
	public void paint(Graphics g, Shape allocation) {
		super.paint(g, allocation);

		Graphics2D g2d = (Graphics2D) g;
		int n = this.getPageCount();
		if (n == 0) {

			/* Paint page frame */
			paintPageFrame(g, 0);

			/* Paint page header */
			paintPageHeader(g, 0);
		}

		Rectangle r = new Rectangle();
		for (int i = 0; i <= n; i++) {

			/* Draw first page */
			if (i == 0) {
				r.x = 0;
				r.y = PageConstants.TOTAL_PAGE_HEIGHT - PAGE_SEPARATOR;
				r.width = PageConstants.TOTAL_PAGE_WIDTH;
				r.height = 2 * PAGE_SEPARATOR;

				/* Paint page separator */
				g2d.setColor(TextPanel.TEXTPANEL_COLOR);
				g2d.fillRect(r.x, r.y, r.width, r.height);

				/* Paint page frame */
				paintPageFrame(g, i);

				/* Paint page header */
				paintPageHeader(g, i);
				continue;
			}

			/*
			 * Draw the remaining pages. The page drawing portion of the code
			 * involves three steps - draw the page separator, then draw the
			 * internal page frame (the internal grey rectangle) and then
			 * finally draw the page header (if required)
			 */

			r.x = 0;
			r.y = (i + 1) * PageConstants.TOTAL_PAGE_HEIGHT - PAGE_SEPARATOR;
			r.width = PageConstants.TOTAL_PAGE_WIDTH;
			r.height = 2 * PAGE_SEPARATOR;

			/* Paint page separator */
			g2d.setColor(TextPanel.TEXTPANEL_COLOR);
			g2d.fillRect(r.x, r.y, r.width, r.height);

			/* Paint page frame */
			paintPageFrame(g, i);

			/* Paint page header */
			paintPageHeader(g, i);
		}
	}

	/* Draw the internal grey rectangle inside the page */
	private void paintPageFrame(Graphics g, int index) {
		Graphics2D g2d = (Graphics2D) g;
		AffineTransform old = g2d.getTransform();
		g2d.translate(PageConstants.PAGE_LEFT_INSET,
				PageConstants.PAGE_TOP_INSET + index
						* PageConstants.TOTAL_PAGE_HEIGHT);
		g2d.setColor(Color.lightGray);

		if (index == 0) {

			/* Draw the page frame of the first page */
			g2d.drawRect(-1, -1, PageConstants.PAGE_WIDTH + 1,
					PageConstants.PAGE_HEIGHT + 1);
		} else {

			int y = 0;
			int nexty = 0;
			int height = 0;

			y = index * PageConstants.TOTAL_PAGE_HEIGHT;
			nexty = (index + 1)
					* PageConstants.PAGE_HEIGHT
					+ (index * (PageConstants.PAGE_TOP_INSET + PageConstants.PAGE_BOTTOM_INSET));
			height = nexty - y;

			/* Draw the page frame of all pages from the first page onwards */
			g2d.drawRect(-1, -1, PageConstants.PAGE_WIDTH + 1, height + 1);
		}

		g2d.setTransform(old);
	}

	/*
	 * If the text pane has a header then the header needs to be drawn above
	 * every page frame of the text pane. This method will always be called
	 * after the page frame painting has been completed. The header has a fixed
	 * height of 36 pixels and should be drawn in between the top of the page
	 * and the page frame. Hence, the page frame has to indicate the
	 * y-coordinate of the top, left corner of the inner rectangle. The top left
	 * corner of the header should be 54 pixels above this corner of the inner
	 * rectangle. That means it should be located (1/4)th way down the page top
	 * inset (18 pixels down from the top of the page frame) as the page top
	 * inset is 72 pixels
	 */
	private void paintPageHeader(Graphics g, int index) {
		Graphics2D g2d = (Graphics2D) g;

		if (header != null) {
			AffineTransform old = g2d.getTransform();
			g2d.translate(PageConstants.PAGE_LEFT_INSET,
					(PageConstants.PAGE_TOP_INSET / 4) + index
							* PageConstants.TOTAL_PAGE_HEIGHT);
			g2d.setColor(Color.lightGray);

			boolean isCaretVisible = header.getCaret().isVisible();
			boolean isCaretSelectionVisible = header.getCaret()
					.isSelectionVisible();
			header.getCaret().setVisible(false);
			header.getCaret().setSelectionVisible(false);

			header.paint(g2d);
			if (index == 0) {

				/*
				 * Regardless of the page frame, the physical location of the
				 * header is always fixed at the top of the first page
				 */
				header.setLocation(PageConstants.PAGE_LEFT_INSET,
						(PageConstants.PAGE_TOP_INSET / 4));
			}

			header.getCaret().setVisible(isCaretVisible);
			header.getCaret().setSelectionVisible(isCaretSelectionVisible);

			g2d.setTransform(old);
		}
	}

	private void validateHF() {
		/* Check if header/footer calculation needs to be done */
		if (TextPane.isHFsizevalid()) {
			return;
		}

		Surface s = (Surface) Surface.getAppSurface();
		TextPanel tpanel = (TextPanel) s.getTextPanel();
		TextPane tpane = (TextPane) tpanel.getTextPane();

		/* Get header height */
		if (tpane.getHeader() == null) {
			this.header = null;
			this.headerHeight = 0;
		} else {
			this.header = tpane.getHeader();
			this.headerHeight = this.header.getSize().height;
		}

		/* Mark header/footer calculation as done */
		TextPane.setHFsizevalid(true);
	}
}
